Render this report with ~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.

library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster

Attaching package: ‘fastcluster’

The following object is masked from ‘package:stats’:

    hclust



Attaching package: ‘WGCNA’

The following object is masked from ‘package:stats’:

    cor
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ readr   1.4.0      ✔ forcats 0.5.1 
✔ purrr   0.3.4      
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:patchwork’:

    align_plots
library(pheatmap)
source("~/Neuraltube/scripts/heatmap4.R")

Load individual seurat and test WGCNA data

The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323), Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323) chicken spinal cord sets. The test WGCNA data are the modules calculated on the integrated data set of all three stages.

se_path <- c("Gg_D05_ctrl_seurat_070323",
             "Gg_D07_ctrl_seurat_070323",
             "Gg_ctrl_1_seurat_070323")

clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>% 
  rename(broad = broad_cluster) %>% 
  select(-marker)

Order of the broad clusters for plotting purposes.

broad_order <- c("progenitors",
      "FP",
      "RP",
      "FP/RP",
      "neurons",
      "OPC",
      "MFOL",
      "pericytes",
      "microglia",
      "blood",
      "vasculature"
      )
my.ses <- list()
col_table <- list()
ord_levels <- list()

for (i in seq(se_path)) {
  # load the data sets
  my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
  annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
                               pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
                               full.names = TRUE))
  
  if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
     stop("Number of clusters must be identical!")
  }
  
  # rename for left join
  annot <- annot %>% 
    mutate(fine = paste(fine, number, sep = "_")) %>% 
    mutate(number = factor(number, levels = 1:nrow(annot))) %>% 
    rename(seurat_clusters = number) 
  
  # cluster order for vln plots
  ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
  
  # create index for color coding
  col_table[[i]] <- annot %>%
    left_join(clust_col, by = "broad") %>% 
    select(c("fine", "color"))
  
  # add cluster annotation to meta data
  my.se@meta.data <- my.se@meta.data %>% 
    rownames_to_column("rowname") %>% 
    left_join(annot, by = "seurat_clusters") %>% 
    mutate(fine = factor(fine, levels = annot$fine)) %>% 
    column_to_rownames("rowname")
  
  my.ses[[i]] <- my.se

}

names(my.ses) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(col_table) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(ord_levels) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")

rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)

# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")

# The subset of clusters in each of the corresponding samples
my.subset=list(c(1:24),
               c(1:26),
               c(1:22))

# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)

#Subset the seurat objects if needed as defined above
for (i in 1:length(my.ses)) {
  my.ses[[i]] = subset(my.ses[[i]], idents = my.subset[[i]])
}

# Take only genes that are present in the all samples
my.dcs = list()

# We go trhough each WGCNA data
for(i in 1:length(WGCNA_data)) {
  #We start with complete modules
  my.dc = WGCNA_data[[i]]$dynamicCols
  
  #Remove the few genes that are not found in the other datasets
  my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[1]]@assays$RNA@data))]
  my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[2]]@assays$RNA@data))]
  my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[3]]@assays$RNA@data))]
  
  my.dc = my.dc[which(my.dc %in% names(table(WGCNA_data[[i]]$dynamicCols))[my.wsub[[i]]])]
  
  my.dcs[[i]] = my.dc
  
}

Module gene correlation

Here, we do a correlation matrix / heatmap, to see which cell clusters group togheter. This helps us to make more detailed dotplots.
This part of the script can still be used to compare several WGCNA datasets in parallel.

# broad cluster color table
all_col <- do.call(rbind, col_table) %>% 
  rownames_to_column("sample") %>% 
  mutate(sample = substr(sample, 1, 11)) %>% 
  mutate(sample_celltype = paste(sample, fine, sep = "_")) %>% 
  select(c("color", "sample_celltype", "sample"))

# take the normalized data, of each single-cell object.
my.datExpr = list()

# Go trough each seurat object
for (i in 1:length(my.ses)) {
 my.datExpr[[i]] = data.frame(my.ses[[i]]@assays$RNA@data)
}

# Calculate, for every cell, in every sample, the average expression of each of the modules.
my.MEs = list()

# For each set of WGCNA modules
for (i in 1:length(WGCNA_data)) {
  
  # Make a new list
  my.MEs[[i]] = list()
  
  # Populate with data frames from each seurat object
  for (j in 1:length(my.ses)) {
    
    # Data frame of average module expression, per cell.
    my.MEs[[i]][[j]] = moduleEigengenes(t(my.datExpr[[j]][names(my.dcs[[i]]),]), colors = my.dcs[[i]])
  }
}

#Calculate average of expression, per sample and cell cluster
my.modavg = list()

for (i in 1:length(WGCNA_data)) {
  
  my.modavg[[i]] = list()
  
  for (j in 1:length(my.ses)) {
    
    #Make the mean per cell clusters
    my.modavg[[i]][[j]] = aggregate(
      my.MEs[[i]][[j]]$averageExpr,
      list(my.ses[[j]]@meta.data$seurat_clusters),
      mean
      )
    # Give the cell clusters meaningful names
    rownames(my.modavg[[i]][[j]]) = paste0(
      my.samplenames[j],
      "_",
      levels(my.ses[[j]]@meta.data$fine)[my.subset[[j]]]
      )
    }
}



#Gather data to plot

my.wgmat = list()

for (i in 1:length(WGCNA_data)) {
  
  #bind the expression data into one dataframe
  my.wgmat[[i]] = data.table::rbindlist(my.modavg[[i]])
  #Get rid of the extra column
  my.wgmat[[i]] = data.frame(my.wgmat[[i]][,-1])
  #Restore the rownames
  rownames(my.wgmat[[i]]) = unlist(sapply(my.modavg[[i]], rownames))

}

#Get a dataframe with annotations for all the samples and colors we need.
my.metam <- list()

for (i in seq(my.ses)) {
  my.metam[[i]] <- my.ses[[i]][[]]
  rownames(my.metam[[i]]) <- paste0(rownames(my.metam[[i]]), "_", i)
}
my.metam <- do.call(rbind, my.metam)

my.metam$orig.ident <- str_replace_all(my.metam$orig.ident, pattern =  "Gg_ctrl_1", "Gg_D10_ctrl")

# my.metam$sample_celltype = paste0(substr(my.metam$orig.ident,7,9),"_",my.metam$seurat_clusters)
my.metam$sample_celltype = paste0(my.metam$orig.ident, "_", my.metam$fine)

my.metam <- my.metam %>% 
  tibble::rownames_to_column(var = "cell_ID") %>%
  dplyr::left_join(all_col, by = "sample_celltype") %>%
  tibble::column_to_rownames(var = "cell_ID")


# get sample colors
my.colsm = c("grey", "grey30", "black")
names(my.colsm) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
 
#The colors for the samples and clusters. First the closest to the heatmap. Add a white space to easy the eye and make less confussing
my.heatcols =list()
for (i in 1:length(my.wgmat)) {
  my.heatcols[[i]] = as.matrix(data.frame(
    cluster= as.character(all_col$color[match(rownames(my.wgmat[[i]]), all_col$sample_celltype)]),
    "." = "white",
    sample= as.character(my.colsm[match(all_col$sample, names(my.colsm))]))
    )
}

rm(my.datExpr, my.MEs, my.dcs)

spearman correlation heatmap

annotations


# names and colors for the heatmap annotation
annot_name <- data.frame(
  "Celltypes" = all_col$sample_celltype,
  "Sample"    = all_col$sample,
  row.names = all_col$sample_celltype)
  

pheat_col_table <- do.call(rbind, col_table) %>% 
  rownames_to_column("sample") %>% 
  mutate(sample = substr(sample, 1,11)) %>% 
  mutate(fine = paste(sample, fine, sep = "_"))

# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]

annot_col <- list(
  Celltypes = pheat_col_table$color,
  Sample = c(Gg_D05_ctrl = "#A4A4A4",
             Gg_D07_ctrl = "#515151",
             Gg_D10_ctrl = "#000000")
  )

names(annot_col[[1]]) <- annot_name$Celltypes

heatmap of module pseudobulk average expression

Load the integrated data set

The integrated data set on which the WGCNA is calculated. We plot it, split by the original cell types from the three original samples.

my.sec = readRDS("~/spinal_cord_paper/data/Gg_devel_int_seurat_250723.rds")

identical(rownames(my.metam), colnames(my.sec))
[1] TRUE
#Which WGCNA dataset are we using?
my.W = 1

#Choose how many groups of cell clusters we want to use. Based on the distance clustering from above
my.clcl = cutree(hclust(as.dist(1-cor(t(my.wgmat[[my.W]])))), k = 5)

my.metam$sample_celltype <- factor(my.metam$sample_celltype, levels = names(my.clcl))
#Set the identities of the integrated data, to the annotated clusters
my.sec = SetIdent(my.sec, value = my.metam$sample_celltype)

p1 <- DimPlot(
  my.sec,
  reduction = "tsne",
  label = TRUE,
  repel = TRUE,
  cols = my.heatcols[[1]][,"cluster"],
  split.by = "orig.ident"
  ) + 
  NoLegend()

p1

Avg. module exp. by stage

tSNE DimPlots showing the average expression of each module by stage.

for (i in seq(my.ses)) {
  # prepare average expression table
  tmp <- avg.mod.eigengenes %>%
    tibble::rownames_to_column("cell_ID") %>%
    dplyr::filter(grepl(paste0("_", i, "$"), cell_ID)) %>%
    dplyr::mutate(cell_ID = stringr::str_remove_all(cell_ID, paste0("_", i))) %>%
    tibble::column_to_rownames("cell_ID")
  
  identical(rownames(tmp), colnames(my.ses[[i]]))
  # add meta data to the seurat objects
  my.ses[[i]] <- AddMetaData(my.ses[[i]], tmp)
}

#max and min expression per module (column max)
mod_max <- apply(avg.mod.eigengenes, MARGIN = 2, FUN = max)[module_order]
mod_min <- apply(avg.mod.eigengenes, MARGIN = 2, FUN = min)[module_order]

modplots <- list()
modplots[[1]] <- list()
modplots[[2]] <- list()
modplots[[3]] <- list()

modules_in_order <- colnames(tmp)[module_order]

# plot the modules split to the stages
for (i in seq(my.ses)) {
  for (j in seq(ncol(tmp))) {
  
    modplots[[i]][[j]]  <- FeaturePlot(
      my.ses[[i]],
      features = modules_in_order[j],
      reduction = "tsne",
      cols = c("grey90", substring(modules_in_order[j], 3))
      ) +
      ggtitle(stringr::str_remove(modules_in_order[j],"^AE")) +
      scale_color_gradient(low="ivory2", high=substring(modules_in_order[j], 3), #colors in the scale
                 # breaks=seq(mod_min[j], mod_max[j], 0.1), #breaks in the scale bar
                 limits=c(mod_min[j], mod_max[j])) #same limits for plots

    
    }
}
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
tmp <- c(modplots[[1]], modplots[[2]], modplots[[3]])

gridExtra::grid.arrange(grobs = tmp, ncol = 3, as.table = FALSE)

AE over time

We plot the average expression of each module in the three stages and the 5 broad cell type clusters present in all 3 stages.

VlnPlots of avg. module exp. by stage and seurat cluster

colored by module

for (i in seq(my.ses)) {
  my.ses[[i]]$seurat_clusters <- factor(
  my.ses[[i]]$seurat_clusters,
  levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
  )

}

vplots <- list()

for (i in seq(my.ses)) {
  
  mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
  
  vplots[[i]] <- VlnPlot(
        my.ses[[i]],
        features = mods[module_order],
        group.by = "seurat_clusters",
        stack = TRUE, flip = TRUE,
        cols = substring(mods, 3)[module_order]) +
    theme(legend.position = "none") +
    geom_hline(yintercept = 0, lty = "dashed")
  
}

vplots[[1]]
vplots[[2]]

vplots[[3]]

colored by cell type

col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
 [1] "#edc919" "#edc919" "#edc919" "#edc919" "#edc919" "#edc919" "#853335" "#99ca3c" "#cd2b91" "#cd2b91" "#cd2b91" "#cd2b91" "#008cb5" "#008cb5" "#008cb5" "#008cb5"
[17] "#008cb5" "#008cb5" "#333399" "#333399" "#f26a2d" "#bebebe"

MN modules

For the figures we specifically select the two MN modules and plot them as Vln plots.

avg. module exp. by stage

v1 <- VlnPlot(my.sec,
        features = mods[module_order], 
        group.by = "orig.ident")
v1

pdf("~/spinal_cord_paper/figures/Fig_1_AE_by_stage.pdf", height = 20, width = 20)
v1
# Date and time of Rendering
Sys.time()

sessionInfo()
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKc291cmNlKCJ+L05ldXJhbHR1YmUvc2NyaXB0cy9oZWF0bWFwNC5SIikKCmBgYAoKIyBMb2FkIGluZGl2aWR1YWwgc2V1cmF0IGFuZCB0ZXN0IFdHQ05BIGRhdGEKClRoZSBpbmRpdmlkdWFsIGRhdGEgc2V0cyBhcmUgdGhlIERheSA1IChHZ19EMDVfY3RybF9zZXVyYXRfMDcwMzIzKSwgRGF5IDcgKEdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMpLCBhbmQgRGF5IDEwIChHZ19jdHJsXzFfc2V1cmF0XzA3MDMyMykgY2hpY2tlbiBzcGluYWwgY29yZCBzZXRzLiBUaGUgdGVzdCBXR0NOQSBkYXRhIGFyZSB0aGUgbW9kdWxlcyBjYWxjdWxhdGVkIG9uIHRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0IG9mIGFsbCB0aHJlZSBzdGFnZXMuCgpgYGB7ciBkYXRhLXNldHN9CnNlX3BhdGggPC0gYygiR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfRDA3X2N0cmxfc2V1cmF0XzA3MDMyMyIsCiAgICAgICAgICAgICAiR2dfY3RybF8xX3NldXJhdF8wNzAzMjMiKQoKY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKSAlPiUgCiAgcmVuYW1lKGJyb2FkID0gYnJvYWRfY2x1c3RlcikgJT4lIAogIHNlbGVjdCgtbWFya2VyKQoKYGBgCgpPcmRlciBvZiB0aGUgYnJvYWQgY2x1c3RlcnMgZm9yIHBsb3R0aW5nIHB1cnBvc2VzLgoKYGBge3Igb3JkZXJpbmd9CmJyb2FkX29yZGVyIDwtIGMoInByb2dlbml0b3JzIiwKICAgICAgIkZQIiwKICAgICAgIlJQIiwKICAgICAgIkZQL1JQIiwKICAgICAgIm5ldXJvbnMiLAogICAgICAiT1BDIiwKICAgICAgIk1GT0wiLAogICAgICAicGVyaWN5dGVzIiwKICAgICAgIm1pY3JvZ2xpYSIsCiAgICAgICJibG9vZCIsCiAgICAgICJ2YXNjdWxhdHVyZSIKICAgICAgKQpgYGAKCgpgYGB7ciBzZXVyYXQtb2JqZWN0cy1hbmQtYW5ub3RhdGlvbnN9Cm15LnNlcyA8LSBsaXN0KCkKY29sX3RhYmxlIDwtIGxpc3QoKQpvcmRfbGV2ZWxzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShzZV9wYXRoKSkgewogICMgbG9hZCB0aGUgZGF0YSBzZXRzCiAgbXkuc2UgPC0gcmVhZFJEUyhwYXN0ZTAoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS8iLCBzZV9wYXRoW2ldLCAiLnJkcyIpKQogIGFubm90IDwtIHJlYWQuY3N2KGxpc3QuZmlsZXMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHN0cl9yZW1vdmUoc2VfcGF0aFtpXSwgIl9zZXVyYXRfXFxkezZ9IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiAgCiAgaWYobGVuZ3RoKHRhYmxlKGFubm90JG51bWJlcikpICE9IGxlbmd0aCh0YWJsZShteS5zZSRzZXVyYXRfY2x1c3RlcnMpKSkgewogICAgIHN0b3AoIk51bWJlciBvZiBjbHVzdGVycyBtdXN0IGJlIGlkZW50aWNhbCEiKQogIH0KICAKICAjIHJlbmFtZSBmb3IgbGVmdCBqb2luCiAgYW5ub3QgPC0gYW5ub3QgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBwYXN0ZShmaW5lLCBudW1iZXIsIHNlcCA9ICJfIikpICU+JSAKICAgIG11dGF0ZShudW1iZXIgPSBmYWN0b3IobnVtYmVyLCBsZXZlbHMgPSAxOm5yb3coYW5ub3QpKSkgJT4lIAogICAgcmVuYW1lKHNldXJhdF9jbHVzdGVycyA9IG51bWJlcikgCiAgCiAgIyBjbHVzdGVyIG9yZGVyIGZvciB2bG4gcGxvdHMKICBvcmRfbGV2ZWxzW1tpXV0gPC0gYW5ub3QkZmluZVtvcmRlcihtYXRjaChhbm5vdCRicm9hZCwgYnJvYWRfb3JkZXIpKV0KICAKICAjIGNyZWF0ZSBpbmRleCBmb3IgY29sb3IgY29kaW5nCiAgY29sX3RhYmxlW1tpXV0gPC0gYW5ub3QgJT4lCiAgICBsZWZ0X2pvaW4oY2x1c3RfY29sLCBieSA9ICJicm9hZCIpICU+JSAKICAgIHNlbGVjdChjKCJmaW5lIiwgImNvbG9yIikpCiAgCiAgIyBhZGQgY2x1c3RlciBhbm5vdGF0aW9uIHRvIG1ldGEgZGF0YQogIG15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lIAogICAgbGVmdF9qb2luKGFubm90LCBieSA9ICJzZXVyYXRfY2x1c3RlcnMiKSAlPiUgCiAgICBtdXRhdGUoZmluZSA9IGZhY3RvcihmaW5lLCBsZXZlbHMgPSBhbm5vdCRmaW5lKSkgJT4lIAogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJyb3duYW1lIikKICAKICBteS5zZXNbW2ldXSA8LSBteS5zZQoKfQoKbmFtZXMobXkuc2VzKSA8LSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCm5hbWVzKGNvbF90YWJsZSkgPC0gYygiR2dfRDA1X2N0cmwiLCAiR2dfRDA3X2N0cmwiLCAiR2dfRDEwX2N0cmwiKQpuYW1lcyhvcmRfbGV2ZWxzKSA8LSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCgpybShteS5zZSwgYW5ub3QpCmBgYAoKYGBge3IgaW5wdXQgZGF0YSwgZWNobz1UUlVFfQojIFRoZSByZWZlcmVuY2UgV0dDTkEgZGF0YS4gV2UgY2FuIGhhdmUgc2V2ZXJhbCwgaWYgd2Ugd2FudCB0byB0ZXN0IG1hbnkgYXQgdGhlIHNhbWUgdGltZQpXR0NOQV9kYXRhID0gbGlzdCgpCldHQ05BX2RhdGFbWzFdXSA9IHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvb3V0cHV0L0dnX2RldmVsX2ludF9zY1dHQ05BXzI1MDcyMy5yZHMiKQpteS53c3ViID1saXN0KCkKbXkud3N1YltbMV1dPSBjKDE6MjIpCgojIHRoZSBuYW1lIG9mIGVhY2ggc2FtcGxlLCBhcyB0aGV5IGFwcGVhciBpbiBteS5maWxlcyBhbmQgaW4gdGhlIG1ldGFkYXRhIG9mIHRoZSBjb21iaW5lZCBvYmplY3QKbXkuc2FtcGxlbmFtZXMgPSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCgojIFRoZSBzdWJzZXQgb2YgY2x1c3RlcnMgaW4gZWFjaCBvZiB0aGUgY29ycmVzcG9uZGluZyBzYW1wbGVzCm15LnN1YnNldD1saXN0KGMoMToyNCksCiAgICAgICAgICAgICAgIGMoMToyNiksCiAgICAgICAgICAgICAgIGMoMToyMikpCgojIFRoaXMgaXMganVzdCB0byBhZGQgYSBsaXR0bGUgYml0IG1vcmUgc2Vuc2UgdG8gdGhlIG1vZHVsZXMsIHNvIHRoYXQgd2UgZG9uJ3QgZ2V0IGp1c3QgYSBjb2xvci4gQ29ycmVzcG9uZHMgdG8gV0dDTkFfZGF0YQpteS5tb2R1bGVuYW1lcyA9IGxpc3QoKQpteS5tb2R1bGVuYW1lc1tbMV1dID0gYygxOjIyKQoKYGBgCgpgYGB7ciBwcmUtcHJvY2VzcywgZWNobz1UUlVFfQoKI1N1YnNldCB0aGUgc2V1cmF0IG9iamVjdHMgaWYgbmVlZGVkIGFzIGRlZmluZWQgYWJvdmUKZm9yIChpIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICBteS5zZXNbW2ldXSA9IHN1YnNldChteS5zZXNbW2ldXSwgaWRlbnRzID0gbXkuc3Vic2V0W1tpXV0pCn0KCiMgVGFrZSBvbmx5IGdlbmVzIHRoYXQgYXJlIHByZXNlbnQgaW4gdGhlIGFsbCBzYW1wbGVzCm15LmRjcyA9IGxpc3QoKQoKIyBXZSBnbyB0cmhvdWdoIGVhY2ggV0dDTkEgZGF0YQpmb3IoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogICNXZSBzdGFydCB3aXRoIGNvbXBsZXRlIG1vZHVsZXMKICBteS5kYyA9IFdHQ05BX2RhdGFbW2ldXSRkeW5hbWljQ29scwogIAogICNSZW1vdmUgdGhlIGZldyBnZW5lcyB0aGF0IGFyZSBub3QgZm91bmQgaW4gdGhlIG90aGVyIGRhdGFzZXRzCiAgbXkuZGMgPSBteS5kY1t3aGljaChuYW1lcyhteS5kYykgJWluJSByb3duYW1lcyhteS5zZXNbWzFdXUBhc3NheXMkUk5BQGRhdGEpKV0KICBteS5kYyA9IG15LmRjW3doaWNoKG5hbWVzKG15LmRjKSAlaW4lIHJvd25hbWVzKG15LnNlc1tbMl1dQGFzc2F5cyRSTkFAZGF0YSkpXQogIG15LmRjID0gbXkuZGNbd2hpY2gobmFtZXMobXkuZGMpICVpbiUgcm93bmFtZXMobXkuc2VzW1szXV1AYXNzYXlzJFJOQUBkYXRhKSldCiAgCiAgbXkuZGMgPSBteS5kY1t3aGljaChteS5kYyAlaW4lIG5hbWVzKHRhYmxlKFdHQ05BX2RhdGFbW2ldXSRkeW5hbWljQ29scykpW215LndzdWJbW2ldXV0pXQogIAogIG15LmRjc1tbaV1dID0gbXkuZGMKICAKfQpgYGAKCiMgTW9kdWxlIGdlbmUgY29ycmVsYXRpb24KCkhlcmUsIHdlIGRvIGEgY29ycmVsYXRpb24gbWF0cml4IC8gaGVhdG1hcCwgdG8gc2VlIHdoaWNoIGNlbGwgY2x1c3RlcnMgZ3JvdXAgdG9naGV0ZXIuIFRoaXMgaGVscHMgdXMgdG8gbWFrZSBtb3JlIGRldGFpbGVkIGRvdHBsb3RzLiAgClRoaXMgcGFydCBvZiB0aGUgc2NyaXB0IGNhbiBzdGlsbCBiZSB1c2VkIHRvIGNvbXBhcmUgc2V2ZXJhbCBXR0NOQSBkYXRhc2V0cyBpbiBwYXJhbGxlbC4gIAoKYGBge3J9CiMgYnJvYWQgY2x1c3RlciBjb2xvciB0YWJsZQphbGxfY29sIDwtIGRvLmNhbGwocmJpbmQsIGNvbF90YWJsZSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBzdWJzdHIoc2FtcGxlLCAxLCAxMSkpICU+JSAKICBtdXRhdGUoc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKSAlPiUgCiAgc2VsZWN0KGMoImNvbG9yIiwgInNhbXBsZV9jZWxsdHlwZSIsICJzYW1wbGUiKSkKYGBgCgpgYGB7ciBtb2R1bGVfZXhwcmVzc2lvbl9kYXRhLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0yMH0KCiMgdGFrZSB0aGUgbm9ybWFsaXplZCBkYXRhLCBvZiBlYWNoIHNpbmdsZS1jZWxsIG9iamVjdC4KbXkuZGF0RXhwciA9IGxpc3QoKQoKIyBHbyB0cm91Z2ggZWFjaCBzZXVyYXQgb2JqZWN0CmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiBteS5kYXRFeHByW1tpXV0gPSBkYXRhLmZyYW1lKG15LnNlc1tbaV1dQGFzc2F5cyRSTkFAZGF0YSkKfQoKIyBDYWxjdWxhdGUsIGZvciBldmVyeSBjZWxsLCBpbiBldmVyeSBzYW1wbGUsIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBvZiB0aGUgbW9kdWxlcy4KbXkuTUVzID0gbGlzdCgpCgojIEZvciBlYWNoIHNldCBvZiBXR0NOQSBtb2R1bGVzCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogICMgTWFrZSBhIG5ldyBsaXN0CiAgbXkuTUVzW1tpXV0gPSBsaXN0KCkKICAKICAjIFBvcHVsYXRlIHdpdGggZGF0YSBmcmFtZXMgZnJvbSBlYWNoIHNldXJhdCBvYmplY3QKICBmb3IgKGogaW4gMTpsZW5ndGgobXkuc2VzKSkgewogICAgCiAgICAjIERhdGEgZnJhbWUgb2YgYXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiwgcGVyIGNlbGwuCiAgICBteS5NRXNbW2ldXVtbal1dID0gbW9kdWxlRWlnZW5nZW5lcyh0KG15LmRhdEV4cHJbW2pdXVtuYW1lcyhteS5kY3NbW2ldXSksXSksIGNvbG9ycyA9IG15LmRjc1tbaV1dKQogIH0KfQoKI0NhbGN1bGF0ZSBhdmVyYWdlIG9mIGV4cHJlc3Npb24sIHBlciBzYW1wbGUgYW5kIGNlbGwgY2x1c3RlcgpteS5tb2RhdmcgPSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogIG15Lm1vZGF2Z1tbaV1dID0gbGlzdCgpCiAgCiAgZm9yIChqIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICAgIAogICAgI01ha2UgdGhlIG1lYW4gcGVyIGNlbGwgY2x1c3RlcnMKICAgIG15Lm1vZGF2Z1tbaV1dW1tqXV0gPSBhZ2dyZWdhdGUoCiAgICAgIG15Lk1Fc1tbaV1dW1tqXV0kYXZlcmFnZUV4cHIsCiAgICAgIGxpc3QobXkuc2VzW1tqXV1AbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycyksCiAgICAgIG1lYW4KICAgICAgKQogICAgIyBHaXZlIHRoZSBjZWxsIGNsdXN0ZXJzIG1lYW5pbmdmdWwgbmFtZXMKICAgIHJvd25hbWVzKG15Lm1vZGF2Z1tbaV1dW1tqXV0pID0gcGFzdGUwKAogICAgICBteS5zYW1wbGVuYW1lc1tqXSwKICAgICAgIl8iLAogICAgICBsZXZlbHMobXkuc2VzW1tqXV1AbWV0YS5kYXRhJGZpbmUpW215LnN1YnNldFtbal1dXQogICAgICApCiAgICB9Cn0KCgoKI0dhdGhlciBkYXRhIHRvIHBsb3QKCm15LndnbWF0ID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAKICAjYmluZCB0aGUgZXhwcmVzc2lvbiBkYXRhIGludG8gb25lIGRhdGFmcmFtZQogIG15LndnbWF0W1tpXV0gPSBkYXRhLnRhYmxlOjpyYmluZGxpc3QobXkubW9kYXZnW1tpXV0pCiAgI0dldCByaWQgb2YgdGhlIGV4dHJhIGNvbHVtbgogIG15LndnbWF0W1tpXV0gPSBkYXRhLmZyYW1lKG15LndnbWF0W1tpXV1bLC0xXSkKICAjUmVzdG9yZSB0aGUgcm93bmFtZXMKICByb3duYW1lcyhteS53Z21hdFtbaV1dKSA9IHVubGlzdChzYXBwbHkobXkubW9kYXZnW1tpXV0sIHJvd25hbWVzKSkKCn0KCiNHZXQgYSBkYXRhZnJhbWUgd2l0aCBhbm5vdGF0aW9ucyBmb3IgYWxsIHRoZSBzYW1wbGVzIGFuZCBjb2xvcnMgd2UgbmVlZC4KbXkubWV0YW0gPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBteS5tZXRhbVtbaV1dIDwtIG15LnNlc1tbaV1dW1tdXQogIHJvd25hbWVzKG15Lm1ldGFtW1tpXV0pIDwtIHBhc3RlMChyb3duYW1lcyhteS5tZXRhbVtbaV1dKSwgIl8iLCBpKQp9Cm15Lm1ldGFtIDwtIGRvLmNhbGwocmJpbmQsIG15Lm1ldGFtKQoKbXkubWV0YW0kb3JpZy5pZGVudCA8LSBzdHJfcmVwbGFjZV9hbGwobXkubWV0YW0kb3JpZy5pZGVudCwgcGF0dGVybiA9ICAiR2dfY3RybF8xIiwgIkdnX0QxMF9jdHJsIikKCiMgbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKHN1YnN0cihteS5tZXRhbSRvcmlnLmlkZW50LDcsOSksIl8iLG15Lm1ldGFtJHNldXJhdF9jbHVzdGVycykKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKG15Lm1ldGFtJG9yaWcuaWRlbnQsICJfIiwgbXkubWV0YW0kZmluZSkKCm15Lm1ldGFtIDwtIG15Lm1ldGFtICU+JSAKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbF9JRCIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYWxsX2NvbCwgYnkgPSAic2FtcGxlX2NlbGx0eXBlIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNlbGxfSUQiKQoKCiMgZ2V0IHNhbXBsZSBjb2xvcnMKbXkuY29sc20gPSBjKCJncmV5IiwgImdyZXkzMCIsICJibGFjayIpCm5hbWVzKG15LmNvbHNtKSA8LSBjKCJHZ19EMDVfY3RybCIsICJHZ19EMDdfY3RybCIsICJHZ19EMTBfY3RybCIpCiAKI1RoZSBjb2xvcnMgZm9yIHRoZSBzYW1wbGVzIGFuZCBjbHVzdGVycy4gRmlyc3QgdGhlIGNsb3Nlc3QgdG8gdGhlIGhlYXRtYXAuIEFkZCBhIHdoaXRlIHNwYWNlIHRvIGVhc3kgdGhlIGV5ZSBhbmQgbWFrZSBsZXNzIGNvbmZ1c3NpbmcKbXkuaGVhdGNvbHMgPWxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgobXkud2dtYXQpKSB7CiAgbXkuaGVhdGNvbHNbW2ldXSA9IGFzLm1hdHJpeChkYXRhLmZyYW1lKAogICAgY2x1c3Rlcj0gYXMuY2hhcmFjdGVyKGFsbF9jb2wkY29sb3JbbWF0Y2gocm93bmFtZXMobXkud2dtYXRbW2ldXSksIGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKV0pLAogICAgIi4iID0gIndoaXRlIiwKICAgIHNhbXBsZT0gYXMuY2hhcmFjdGVyKG15LmNvbHNtW21hdGNoKGFsbF9jb2wkc2FtcGxlLCBuYW1lcyhteS5jb2xzbSkpXSkpCiAgICApCn0KCnJtKG15LmRhdEV4cHIsIG15Lk1FcywgbXkuZGNzKQpgYGAKCiMjIHNwZWFybWFuIGNvcnJlbGF0aW9uIGhlYXRtYXAKCiMjIyBhbm5vdGF0aW9ucwoKYGBge3IgYW5ub3QtbGlzdH0KCiMgbmFtZXMgYW5kIGNvbG9ycyBmb3IgdGhlIGhlYXRtYXAgYW5ub3RhdGlvbgphbm5vdF9uYW1lIDwtIGRhdGEuZnJhbWUoCiAgIkNlbGx0eXBlcyIgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSwKICAiU2FtcGxlIiAgICA9IGFsbF9jb2wkc2FtcGxlLAogIHJvdy5uYW1lcyA9IGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKQogIAoKcGhlYXRfY29sX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIGNvbF90YWJsZSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBzdWJzdHIoc2FtcGxlLCAxLDExKSkgJT4lIAogIG11dGF0ZShmaW5lID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKQoKIyBtYXRjaCBjb2xvciB0YWJsZSB3aXRoIGFubm90YXRpb24KcGhlYXRfY29sX3RhYmxlIDwtIHBoZWF0X2NvbF90YWJsZVttYXRjaChhbm5vdF9uYW1lJENlbGx0eXBlcywgcGhlYXRfY29sX3RhYmxlJGZpbmUpLF0KCmFubm90X2NvbCA8LSBsaXN0KAogIENlbGx0eXBlcyA9IHBoZWF0X2NvbF90YWJsZSRjb2xvciwKICBTYW1wbGUgPSBjKEdnX0QwNV9jdHJsID0gIiNBNEE0QTQiLAogICAgICAgICAgICAgR2dfRDA3X2N0cmwgPSAiIzUxNTE1MSIsCiAgICAgICAgICAgICBHZ19EMTBfY3RybCA9ICIjMDAwMDAwIikKICApCgpuYW1lcyhhbm5vdF9jb2xbWzFdXSkgPC0gYW5ub3RfbmFtZSRDZWxsdHlwZXMKCmBgYAoKYGBge3IsICBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTN9CmhlYXRfY29sIDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpCgp0b3Bsb3QgPC0gY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpCgojQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSAxLWNvciwgYW5kIHRoZW4gY2FsY3VsYXRlIGRlbmRvZ3JhbXMgdG8gY2x1c3RlciB0aGUgY2x1c3RlcnMKbXkuaGNsdXN0ID0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIikpKQoKIyBsb3dlciBsaW1pdCB0byBzY2FsZSBjb2xvciBiYXIKbG93X2xpbWl0IDwtIDEwMCAtIGFicyhyb3VuZChyYW5nZSh0b3Bsb3QpWzFdKjEwMCkpCgpwaGVhdG1hcCh0b3Bsb3QscmV2QyA9IFQsCiAgICAgICAgICAgICAgICAgbWFpbiA9ICJzcGVhcm1hbiBjb3JyZWxhdGlvbiBvZiBhdmVyYWdlIG1vZHVsZSBlaWdlbmdlbmUgZXhwcmVzc2lvbiBieSBjbHVzdGVyXG4gRGVuZHJvZ3JhbSBiYXNlZCBvbiAnZGlzdGFuY2UgMS1jb3InIiwKICAgICAgICAgICAgICAgICBmb250c2l6ZSA9IDgsICAKICAgICAgICAgICAgICAgICBjb2xvciA9IGhlYXRfY29sKDIwMClbbG93X2xpbWl0OjIwMF0sCiAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gbXkuaGNsdXN0LAogICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IG15LmhjbHVzdCwKICAgICAgICAgICAgICAgICB0cmVlaGVpZ2h0X3JvdyA9IDAsIAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gYW5ub3RfbmFtZSwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90X2NvbCwKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZCA9IEYsCiAgICAgICAgICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEKICAgICAgICAgICAgICAgICApCgpjb2xiYXIgPC0gYyhtaW4oY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSwKICAgICAgICAgICAgbWF4KGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpCgojQ2FsY3VsYXRlIHRoZSBkaXN0YW5jZSAxLWNvciwgYW5kIHRoZW4gY2FsY3VsYXRlIGRlbmRvZ3JhbXMgdG8gY2x1c3RlciB0aGUgY2x1c3RlcnMKbXkuaGNsdXN0ID0gYXMuZGVuZHJvZ3JhbShoY2x1c3QoYXMuZGlzdCgxLWNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpKQoKI1Bsb3QhCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX21vZHVsZV9nZW5lX2hlYXRtYXBfc3BlYXJtYW4ucGRmIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gMTEpCmhlYXRtYXAuNChjb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIiksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBsaGVpPWMoMC4yLDEuNiksCiAgICAgICAgICBsd2lkPWMoMC4yLDEuNiksCiAgICAgICAgICBzY2FsZSA9ICJub25lIixtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBjZXhDb2wgPSAwLjc1LAogICAgICAgICAgcmV2QyA9IFRSVUUsCiAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gbXkuaGVhdGNvbHNbWzFdXSwKICAgICAgICAgIFJvd1NpZGVDb2xvcnMgPSB0KG15LmhlYXRjb2xzW1sxXV1bLDM6MV0pLFJvd3YgPSBteS5oY2x1c3QsIENvbHYgPSBteS5oY2x1c3QpCgoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbW9kdWxlX2dlbmVfaGVhdG1hcF9jb2xvcl9rZXlfc3BlYXJtYW4ucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTApCiMgaGVhdG1hcCBzaW5jZSBjb2xvciBrZXkgaXMgbWlzc2luZyBpbiBmaXJzdCBoZWF0bWFwCmhlYXRtYXAuNChyYmluZChjb2xiYXIsIGNvbGJhciksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBsaGVpPWMoMC4yLDEuNiksCiAgICAgICAgICBsd2lkPWMoMC4yLDEuNiksCiAgICAgICAgICBzY2FsZSA9ICJub25lIixtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBjZXhDb2wgPSAwLjc1KQoKYGBgCgojIyBoZWF0bWFwIG9mIG1vZHVsZSBwc2V1ZG9idWxrIGF2ZXJhZ2UgZXhwcmVzc2lvbgoKYGBge3IsICBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTB9CiMgbW9kdWxlIGNvbG9ycwpteS5jb2xjb2xzID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgobXkud2dtYXQpKSB7CiAgICBteS5jb2xjb2xzW1tpXV0gPSBhcy5tYXRyaXgobmFtZXModGFibGUoV0dDTkFfZGF0YVtbaV1dJGR5bmFtaWNDb2xzKSkpCn0KCmF2Zy5tb2QuZWlnZW5nZW5lcyA8LSBXR0NOQV9kYXRhW1sxXV0kc2MuTUVMaXN0JGF2ZXJhZ2VFeHByCnJtKFdHQ05BX2RhdGEpCgpodG1wIDwtIGhlYXRtYXAuNChhcy5tYXRyaXgobXkud2dtYXRbWzFdXSksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IG15LmNvbGNvbHNbWzFdXSwgCiAgICAgICAgICBSb3dTaWRlQ29sb3JzID0gdChteS5oZWF0Y29sc1tbMV1dKSkKCm1vZHVsZV9vcmRlciA8LSBodG1wW1siY29sSW5kIl1dCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EZXZlbF9tb2R1bGVfdl9jbHVzdGVyc19oZWF0bWFwLnBkZiIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMTApCmhlYXRtYXAuNChhcy5tYXRyaXgobXkud2dtYXRbWzFdXSksCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKShuID0gMTAwMCksCiAgICAgICAgICB0cmFjZT0ibm9uZSIsCiAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IG15LmNvbGNvbHNbWzFdXSwgCiAgICAgICAgICBSb3dTaWRlQ29sb3JzID0gdChteS5oZWF0Y29sc1tbMV1dKSkKCmBgYAoKCgojIExvYWQgdGhlIGludGVncmF0ZWQgZGF0YSBzZXQKClRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0IG9uIHdoaWNoIHRoZSBXR0NOQSBpcyBjYWxjdWxhdGVkLiBXZSBwbG90IGl0LCBzcGxpdCBieSB0aGUgb3JpZ2luYWwgY2VsbCB0eXBlcyBmcm9tIHRoZSB0aHJlZSBvcmlnaW5hbCBzYW1wbGVzLiAKCmBgYHtyIGRpbXBsb3RzLCBmaWcud2lkdGg9MTB9CiMgVGhpcyBpcyBhIGZpbGUgb2YgYWxsIHRoZSBjb21iaW5lZCBtb3VzZSBkYXRhc2V0cywgbm9ybWFsaXplZCBhbmQgc3VjaC4KbXkuc2VjID0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2RldmVsX2ludF9zZXVyYXRfMjUwNzIzLnJkcyIpCgppZGVudGljYWwocm93bmFtZXMobXkubWV0YW0pLCBjb2xuYW1lcyhteS5zZWMpKQoKI1doaWNoIFdHQ05BIGRhdGFzZXQgYXJlIHdlIHVzaW5nPwpteS5XID0gMQoKI0Nob29zZSBob3cgbWFueSBncm91cHMgb2YgY2VsbCBjbHVzdGVycyB3ZSB3YW50IHRvIHVzZS4gQmFzZWQgb24gdGhlIGRpc3RhbmNlIGNsdXN0ZXJpbmcgZnJvbSBhYm92ZQpteS5jbGNsID0gY3V0cmVlKGhjbHVzdChhcy5kaXN0KDEtY29yKHQobXkud2dtYXRbW215LlddXSkpKSksIGsgPSA1KQoKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlIDwtIGZhY3RvcihteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUsIGxldmVscyA9IG5hbWVzKG15LmNsY2wpKQojU2V0IHRoZSBpZGVudGl0aWVzIG9mIHRoZSBpbnRlZ3JhdGVkIGRhdGEsIHRvIHRoZSBhbm5vdGF0ZWQgY2x1c3RlcnMKbXkuc2VjID0gU2V0SWRlbnQobXkuc2VjLCB2YWx1ZSA9IG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSkKCnAxIDwtIERpbVBsb3QoCiAgbXkuc2VjLAogIHJlZHVjdGlvbiA9ICJ0c25lIiwKICBsYWJlbCA9IFRSVUUsCiAgcmVwZWwgPSBUUlVFLAogIGNvbHMgPSBteS5oZWF0Y29sc1tbMV1dWywiY2x1c3RlciJdLAogIHNwbGl0LmJ5ID0gIm9yaWcuaWRlbnQiCiAgKSArIAogIE5vTGVnZW5kKCkKCnAxCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EZXZlbF9zcGxpdF90c25lLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTUpCiNQbG90IHNwbGl0IHRzbmUKcDEKYGBgCgojIEF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UKCnRTTkUgRGltUGxvdHMgc2hvd2luZyB0aGUgYXZlcmFnZSBleHByZXNzaW9uIG9mIGVhY2ggbW9kdWxlIGJ5IHN0YWdlLgoKYGBge3IgQUUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00MH0KCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICMgcHJlcGFyZSBhdmVyYWdlIGV4cHJlc3Npb24gdGFibGUKICB0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzICU+JQogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoZ3JlcGwocGFzdGUwKCJfIiwgaSwgIiQiKSwgY2VsbF9JRCkpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX0lEID0gc3RyaW5ncjo6c3RyX3JlbW92ZV9hbGwoY2VsbF9JRCwgcGFzdGUwKCJfIiwgaSkpKSAlPiUKICAgIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsX0lEIikKICAKICBpZGVudGljYWwocm93bmFtZXModG1wKSwgY29sbmFtZXMobXkuc2VzW1tpXV0pKQogICMgYWRkIG1ldGEgZGF0YSB0byB0aGUgc2V1cmF0IG9iamVjdHMKICBteS5zZXNbW2ldXSA8LSBBZGRNZXRhRGF0YShteS5zZXNbW2ldXSwgdG1wKQp9CgojbWF4IGFuZCBtaW4gZXhwcmVzc2lvbiBwZXIgbW9kdWxlIChjb2x1bW4gbWF4KQptb2RfbWF4IDwtIGFwcGx5KGF2Zy5tb2QuZWlnZW5nZW5lcywgTUFSR0lOID0gMiwgRlVOID0gbWF4KVttb2R1bGVfb3JkZXJdCm1vZF9taW4gPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW4pW21vZHVsZV9vcmRlcl0KCm1vZHBsb3RzIDwtIGxpc3QoKQptb2RwbG90c1tbMV1dIDwtIGxpc3QoKQptb2RwbG90c1tbMl1dIDwtIGxpc3QoKQptb2RwbG90c1tbM11dIDwtIGxpc3QoKQoKbW9kdWxlc19pbl9vcmRlciA8LSBjb2xuYW1lcyh0bXApW21vZHVsZV9vcmRlcl0KCiMgcGxvdCB0aGUgbW9kdWxlcyBzcGxpdCB0byB0aGUgc3RhZ2VzCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIGZvciAoaiBpbiBzZXEobmNvbCh0bXApKSkgewogIAogICAgbW9kcGxvdHNbW2ldXVtbal1dICA8LSBGZWF0dXJlUGxvdCgKICAgICAgbXkuc2VzW1tpXV0sCiAgICAgIGZlYXR1cmVzID0gbW9kdWxlc19pbl9vcmRlcltqXSwKICAgICAgcmVkdWN0aW9uID0gInRzbmUiLAogICAgICBjb2xzID0gYygiZ3JleTkwIiwgc3Vic3RyaW5nKG1vZHVsZXNfaW5fb3JkZXJbal0sIDMpKQogICAgICApICsKICAgICAgZ2d0aXRsZShzdHJpbmdyOjpzdHJfcmVtb3ZlKG1vZHVsZXNfaW5fb3JkZXJbal0sIl5BRSIpKSArCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0iaXZvcnkyIiwgaGlnaD1zdWJzdHJpbmcobW9kdWxlc19pbl9vcmRlcltqXSwgMyksICNjb2xvcnMgaW4gdGhlIHNjYWxlCiAgICAgICAgICAgICAgICAgIyBicmVha3M9c2VxKG1vZF9taW5bal0sIG1vZF9tYXhbal0sIDAuMSksICNicmVha3MgaW4gdGhlIHNjYWxlIGJhcgogICAgICAgICAgICAgICAgIGxpbWl0cz1jKG1vZF9taW5bal0sIG1vZF9tYXhbal0pKSAjc2FtZSBsaW1pdHMgZm9yIHBsb3RzCgogICAgCiAgICB9Cn0KCnRtcCA8LSBjKG1vZHBsb3RzW1sxXV0sIG1vZHBsb3RzW1syXV0sIG1vZHBsb3RzW1szXV0pCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHRtcCwgbmNvbCA9IDMsIGFzLnRhYmxlID0gRkFMU0UpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EZXZlbF9tb2R1bGVzX0FFX3Bsb3RzLnBkZiIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDcwKQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHRtcCwgbmNvbCA9IDMsIGFzLnRhYmxlID0gRkFMU0UpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9NTl9tb2RfdHNuZS5wZGYiLCBoZWlnaHQgPSA2LCB3aWR0aCA9IDEyKQptb2RwbG90c1tbMV1dW1syMl1dICsgbW9kcGxvdHNbWzFdXVtbNl1dIHwKICBtb2RwbG90c1tbMl1dW1syMl1dICsgbW9kcGxvdHNbWzJdXVtbNl1dIHwKICBtb2RwbG90c1tbM11dW1syMl1dICsgbW9kcGxvdHNbWzNdXVtbNl1dCgpgYGAKCiMgQUUgb3ZlciB0aW1lCgpXZSBwbG90IHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgaW4gdGhlIHRocmVlIHN0YWdlcyBhbmQgdGhlIDUgYnJvYWQgY2VsbCB0eXBlIGNsdXN0ZXJzIHByZXNlbnQgaW4gYWxsIDMgc3RhZ2VzLgoKYGBge3IgQUUtb3Zlci10aW1lLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMn0KIyBtb2R1bGUgYW5ub3RhdGlvbnMKbW9kX2Fubm90IDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL0dnX2RldmVsX2ludF9zY1dHQ05BX21vZHVsZV9hbm5vdGF0aW9uLmNzdiIpICU+JQogIGRwbHlyOjptdXRhdGUobW9kdWxlID0gc3RyX3JlcGxhY2VfYWxsKG1vZHVsZSwgIlxcZHsxLDJ9XFxfIiwgIkFFIikpCgptZXRhIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbWV0YVtbaV1dIDwtIG15LnNlc1tbaV1dQG1ldGEuZGF0YSAlPiUKICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBwYXN0ZTAoY2VsbF9JRCwgIl8iLCBpKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGMoImNlbGxfSUQiLCAiYnJvYWQiKSkKfQoKbWV0YSA8LSBkby5jYWxsKHJiaW5kLCBtZXRhKQoKIyBtZWFuIGF2ZXJhZ2UgZXhwcmVzc2lvbiBieSBzdGFnZQptZWFuX0FFIDwtIGF2Zy5tb2QuZWlnZW5nZW5lcyAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogIGRwbHlyOjptdXRhdGUoc3RhZ2UgPSBzdHJpbmdyOjpzdHJfc3ViKGNlbGxfSUQsIC0xKSkgJT4lCiAgZHBseXI6Om11dGF0ZShzdGFnZSA9IGZhY3RvcihzdGFnZSwgbGV2ZWxzID0gYygxOjMpLCBsYWJlbHMgPSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihtZXRhLCBieSA9ICJjZWxsX0lEIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImNlbGxfSUQiKSAlPiUKICB0aWR5cjo6dW5pdGUoInN0YWdlX2NsIiwgc3RhZ2UsIGJyb2FkLCBzZXAgPSAiXyIpICU+JQogIGRwbHlyOjpncm91cF9ieShzdGFnZV9jbCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZV9lYWNoKG1lYW4pICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZ2F0aGVyKGtleT0ibW9kdWxlIiwgdmFsdWUgPSAiQUUiLCAtc3RhZ2VfY2wpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4obW9kX2Fubm90WywgYygxLDMpXSwgYnkgPSAibW9kdWxlIikgJT4lCiAgdGlkeXI6OnNlcGFyYXRlKCJzdGFnZV9jbCIsIGMoInN0YWdlIiwgImJyb2FkIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpICU+JQogIGRwbHlyOjpmaWx0ZXIoYnJvYWQgJWluJSBjKCJwcm9nZW5pdG9ycyIsICJuZXVyb25zIiwgIlJQIiwgIkZQIiwgInBlcmljeXRlcyIpKQoKbGFiZWxzX2RvdHBsb3QgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShtb2R1bGVzX2luX29yZGVyLCAiXkFFIikKbmFtZXMobGFiZWxzX2RvdHBsb3QpIDwtIG1vZHVsZXNfaW5fb3JkZXIKCm1lYW5fbW9kIDwtIGdncGxvdChkYXRhID0gbWVhbl9BRSwKICBhZXMoCiAgICB4ID0gc3RhZ2UsCiAgICB5ID0gQUUsCiAgICBjb2xvciA9IGZhY3Rvcihicm9hZCwgbGV2ZWxzID0gYygicHJvZ2VuaXRvcnMiLCAibmV1cm9ucyIsICJSUCIsICJGUCIsICJwZXJpY3l0ZXMiKSksCiAgICBncm91cCA9IGJyb2FkLAogICAgbGFiZWwgPSBhbm5vdGF0aW9uCiAgICApCiAgKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNlZGM5MTkiLCAiI2NkMmI5MSIsICIjOTljYTNjIiwgIiM4NTMzMzUiLCAiI2YyNmEyZCIpKSArCiAgdGhlbWVfYncoKSArCiAgIyBmYWNldCB3cmFwIHdpdGggcmVvcmRlcmVkIGZhY3RvcnMKICBmYWNldF93cmFwKHZhcnMoZmFjdG9yKG1vZHVsZSwgbGV2ZWxzID0gdW5pcXVlKG1lYW5fQUUkbW9kdWxlKVttb2R1bGVfb3JkZXJdKSksCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV95IiwKICAgICAgICAgICAgIG5yb3cgPSA0LAogICAgICAgICAgICAgbmNvbCA9IDYpICsKICBsYWJzKGNvbG9yID0gImJyb2FkIikgKwogIGdndGl0bGUoIkF2ZXJhZ2UgbW9kdWxlIGV4cHJlc3Npb24gYnkgc3RhZ2UiKQoKcGxvdGx5OjpnZ3Bsb3RseShtZWFuX21vZCkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX21lYW5fbW9kX0FFLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMTIpCiNQbG90IHNwbGl0IHRzbmUKbWVhbl9tb2QKYGBgCgojIFZsblBsb3RzIG9mIGF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UgYW5kIHNldXJhdCBjbHVzdGVyIAoKIyMgY29sb3JlZCBieSBtb2R1bGUKCmBgYHtyIGF2ZXJhZ2UtbW9kdWxlLWV4cHJlc3Npb24tcGVyLWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KCiMgcmVvcmRlciBzZXVyYXQgY2x1c3RlcnMKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzIDwtIGZhY3RvcigKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMsCiAgbGV2ZWxzID0gbGV2ZWxzKG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycylbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldCiAgKQoKfQoKdnBsb3RzIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzW1tpXV0gPC0gVmxuUGxvdCgKICAgICAgICBteS5zZXNbW2ldXSwKICAgICAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICAgICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQogIAp9Cgp2cGxvdHNbWzFdXQp2cGxvdHNbWzJdXQp2cGxvdHNbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfY2x1c3Rlcl9tb2Rjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCmBgYAoKIyMgY29sb3JlZCBieSBjZWxsIHR5cGUKCmBgYHtyIHZsbi1ieS1jbHVzdGVyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MjB9CmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikKCnZwbG90c19pZCA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogICAgCiAgbW9kcyA8LSBjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pW2dyZXAoIl5BRSIsY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKSldCiAgCiAgdnBsb3RzX2lkW1tpXV0gPC0gVmxuUGxvdCgKICAgIG15LnNlc1tbaV1dLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGZpbGwuYnkgPSAiaWRlbnQiLAogICAgY29scyA9IGNvbF90YWJsZVtbaV1dJGNvbG9yW2FzLmludGVnZXIoc3RyX2V4dHJhY3Qob3JkX2xldmVsc1tbaV1dLCAiXFxkezEsMn0kIikpXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKfQoKdnBsb3RzX2lkW1sxXV0KdnBsb3RzX2lkW1syXV0KdnBsb3RzX2lkW1szXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X2NsdXN0ZXJfY2x1Y29sLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDEwKQp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQpgYGAKCiMgTU4gbW9kdWxlcwoKRm9yIHRoZSBmaWd1cmVzIHdlIHNwZWNpZmljYWxseSBzZWxlY3QgdGhlIHR3byBNTiBtb2R1bGVzIGFuZCBwbG90IHRoZW0gYXMgVmxuIHBsb3RzLgoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQp0bXAgPC0gYXZnLm1vZC5laWdlbmdlbmVzIAogIAppZGVudGljYWwocm93bmFtZXModG1wKSwgY29sbmFtZXMobXkuc2VjKSkKIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBpbnQgc2V1cmF0IG9iamVjdApteS5zZWMgPC0gQWRkTWV0YURhdGEobXkuc2VjLCB0bXApCm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIG15Lm1ldGFtW2MoImZpbmUiLCAic2FtcGxlX2NlbGx0eXBlIildKQoKY3VzdG9tX29yZGVyIDwtIGMocGFzdGUobmFtZXMob3JkX2xldmVscylbMV0sIG9yZF9sZXZlbHNbWzFdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbMl0sIG9yZF9sZXZlbHNbWzJdXSwgc2VwID0gJ18nKSwKICAgICAgICAgICAgICAgICAgcGFzdGUobmFtZXMob3JkX2xldmVscylbM10sIG9yZF9sZXZlbHNbWzNdXSwgc2VwID0gJ18nKSkKCm15LnNlYyRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKAogIG15LnNlYyRzYW1wbGVfY2VsbHR5cGUsCiAgbGV2ZWxzID0gY3VzdG9tX29yZGVyCiAgKQoKdmxuX2luZCA8LSBWbG5QbG90KG15LnNlYywKICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQUVkYXJrcmVkIiwgIkFFZ3JlZW4iKSwKICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICAgICAgICAgICAgICAgICBzdGFjayA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBmbGlwID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIGNvbHMgPSBjKCJkYXJrcmVkIiwgImdyZWVuIikscHQuc2l6ZSA9IDEKICAgICAgICAgICAgICAgICAgKSArCiAgICBOb0xlZ2VuZCgpCgp2bG5faW5kCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX01Ocy5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDIwKQp2bG5faW5kCmBgYAoKYGBge3J9ClZsblBsb3QoCiAgICBteS5zZWMsCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQoKCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9ieV9jbHVzdGVyX2ludGVncmF0ZWRfZGF0YS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAzMCkKVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKYGBgCgojIGF2Zy4gbW9kdWxlIGV4cC4gYnkgc3RhZ2UKCmBgYHtyIEFFLWJ5LXN0YWdlLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjB9CnYxIDwtIFZsblBsb3QobXkuc2VjLAogICAgICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLCAKICAgICAgICBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKdjEKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X3N0YWdlLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwKQp2MQoKYGBgCgoKYGBge3J9CiMgRGF0ZSBhbmQgdGltZSBvZiBSZW5kZXJpbmcKU3lzLnRpbWUoKQoKc2Vzc2lvbkluZm8oKQpgYGAKCg==